/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package phd.dupenois.es.nplusn;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 *
 * @author mpd209
 */
public abstract class Solution implements SolutionInterface{
    private final SolutionClass solutionClass;
    private final HashMap<String, ObjectiveResult> values;
//    private final HashMap<String, Baseline> baselines;
    public Solution(SolutionClass solutionClass){
        this.solutionClass = solutionClass;
        this.values = new HashMap<String, ObjectiveResult>();
//        this.baselines = new HashMap<String, Baseline>();
    }

    public final void setValue(String name, Number value){
        if(!this.solutionClass.objectiveExists(name)) throw new
                IllegalArgumentException("Name '"+name+"' not found.");
        if(!this.solutionClass.getObjective(name).isValueValid(value)) throw new
                IllegalArgumentException("Value '"+value+"' is an invalid class.");
        ObjectiveResult result = new ObjectiveResult(value, this.solutionClass.getObjective(name));
        this.values.put(name, result);
    }
    public final ObjectiveResult getObjectiveResult(String name){
        if(!this.solutionClass.objectiveExists(name)) throw new
                IllegalArgumentException("Name '"+name+"' not found.");
        return this.values.get(name);
    }
    public final Number getValue(String name){
        return getObjectiveResult(name).getValue();
    }

    public final ObjectiveResult[] getObjectiveResults(){
        return this.values.values().toArray(new ObjectiveResult[values.size()]);
    }


    public final boolean aboveBaselines(){
        Set<String> baselineNames = this.solutionClass.getBaselineNames();
        Iterator<String> baselineNameIter = baselineNames.iterator();
        String baselineName;
        Baseline baseline;
        String objectiveName;
        ObjectiveResult myResult;
        boolean aboveBaselines = true;
        while(baselineNameIter.hasNext() && aboveBaselines){
            baselineName = baselineNameIter.next();
            baseline = this.solutionClass.getBaseline(baselineName);
            objectiveName = baseline.getObjective().getName();
            myResult = getObjectiveResult(objectiveName);
            aboveBaselines = (baseline.isObjectiveResultAboveBaseline(myResult));
        }
        return aboveBaselines;
    }

    public final Domination compareTo(Solution solution){
        if(!solution.getSolutionClass().equals(getSolutionClass())) throw new
                IllegalArgumentException("Solution classes do not match.");
        Set<String> names = this.values.keySet();
        Iterator<String> nameIter = names.iterator();
        
        String name;
        ObjectiveResult myResult;
        ObjectiveResult otherResult;
        Domination valueDomination;
        int maxScore = names.size();
        int minScore = -names.size();
        int score=0;
        while(nameIter.hasNext()){
            name = nameIter.next();
            myResult = getObjectiveResult(name);
            otherResult = solution.getObjectiveResult(name);
            valueDomination = myResult.compareValue(otherResult);
            if(valueDomination.isDominated())score--;
            else if(valueDomination.isDominates())score++;
        }
        Domination totalDomination = Domination.MUTUALLY_NON_DOMINATING;
        if(score==maxScore) totalDomination = Domination.DOMINATES;
        else if(score==minScore) totalDomination = Domination.DOMINATED;
        return totalDomination;
    }

    public final <T extends Solution> T[] castArray(Solution[] solArray){
        T[] array = (T[]) Array.newInstance(this.getClass(), solArray.length);
        for(int i=0; i< solArray.length; i++){
            array[i] = (T)solArray[i];
        }
        return array;
    }

    /**
     * @return the solutionClass
     */
    public final SolutionClass getSolutionClass() {
        return this.solutionClass;
    }

    public final Objective getObjective(String name) {
        return this.solutionClass.getObjective(name);
    }
    public final <T extends Number> ObjectiveResult<T> createObjectiveResult(String name, T value) {
        return new ObjectiveResult<T>(value, getObjective(name));
    }
    /**
     * @return the values
     
    public final HashMap<String, Number> getValues() {
        return this.values;
    }
    */
}
